﻿//NorManager - OpenTTD Dedicated server manager
//Copyright (C) 2014 Tomáš Bosek

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Diagnostics;
using System.Threading;
using System.IO;
using System.Collections.Generic;

namespace NorManager
{
    delegate void SessionReaderHandler(string line);

    /// <summary>
    /// SessionReader reads process output.
    /// </summary>
    public class SessionReader
    {
        private Process process = null;
        private bool readingStarted = false;

        private Thread standardRead = null;
        private Thread errorRead = null;

        private SessionReaderHandler received;

        /// <summary>
        /// Initializes SessionReader.
        /// </summary>
        internal SessionReader()
        {
            process = Session.Instance.SessionProc;

            if (Session.Instance.SessionProc.HasExited)
            {
                Logger.Instance.Log("Cannot listen closed process!");
                return;
            }
            else if (readingStarted)
            {
                Logger.Instance.Log("You are already listening to this output!");
                return;
            }

            //Handle received messages together
            received += delegate(string line)
            {
                Logger.Instance.Log(line, ConsoleColor.Gray);

                //Better save actual filter to list to avoid NullReferenceException in case of reload or something
                List<Filter> filterList = Session.Instance.FilterList;
                foreach (Filter filter in filterList)
                {
                    //Better do it in try. One never knows, who can write dangerous script
                    try
                    {
                        if (line.Contains(filter.Pattern))
                            filter.Action.Invoke(line);
                    }
                    catch (Exception ex)
                    {
                        Logger.Instance.Log(String.Format("Exception thrown on filter with pattern \"{0}\". Exception: {1}", filter.Pattern, ex.Message), ConsoleColor.White);
                    }
                }
            };

            //Thread for reading both outputs
            ParameterizedThreadStart outputReader = delegate(object stream)
            {
                StreamReader reader = (StreamReader)stream;

                while (readingStarted)
                {
                    if (process.HasExited)
                        break;
                    try
                    {
                        string line = reader.ReadLine();
                        if(line != null)
                            received(line);
                    }
                    catch (Exception) { break; }       //There may be an exception during sync reader.ReadLine()
                }
            };
            standardRead = new Thread(outputReader);
            errorRead = new Thread(outputReader);

            //Reading input. Do not let anyone start new SessionReader on this Session.
            readingStarted = true;

            //Start threads for standard and error outputs
            standardRead.Start(process.StandardOutput);
            errorRead.Start(process.StandardError);
        }

        /// <summary>
        /// Closes reader threads.
        /// </summary>
        internal void CloseReader()
        {
            if (process != null && !process.HasExited && readingStarted)
            {
                readingStarted = false;
            }
        }
    }
}
